use crate::dto;
use crate::http::client::{error, rpc};
use crate::http::route;
use reqwest::Method;
use std::sync::Arc;

pub struct AuthClient {
    inner: Arc<rpc::Client>,
}

impl AuthClient {
    pub fn new(rpc_client: Arc<rpc::Client>) -> Self {
        Self { inner: rpc_client }
    }

    pub async fn login(
        &self,
        username: &str,
        password: &str,
    ) -> Result<dto::auth::Token, error::Error> {
        let data = self
            .login_apply(&dto::auth::LoginApplyRequest {
                username: username.to_string(),
            })
            .await?;

        let public_key = wanmesh_common::auth::PublicKey::try_from(data.public_key.as_slice())?;

        let password_crypto = public_key.encrypt(password.as_bytes())?;

        self.login_submit(&dto::auth::LoginSubmitRequest {
            apply_id: data.apply_id,
            username: username.to_string(),
            password_crypto,
        })
        .await
    }

    pub async fn login_apply(
        &self,
        request: &dto::auth::LoginApplyRequest,
    ) -> Result<dto::auth::LoginResponseData, error::Error> {
        self.inner
            .call(
                route::PATH_AUTH_LOGIN_APPLY,
                Method::POST,
                request,
                rpc::CallOptions { auth_disable: true },
            )
            .await
    }

    pub async fn login_submit(
        &self,
        request: &dto::auth::LoginSubmitRequest,
    ) -> Result<dto::auth::Token, error::Error> {
        self.inner
            .call(
                route::PATH_AUTH_LOGIN_SUBMIT,
                Method::POST,
                request,
                rpc::CallOptions { auth_disable: true },
            )
            .await
    }

    pub async fn refresh_token(
        &self,
        request: &dto::auth::RefreshTokenRequest,
    ) -> Result<dto::auth::Token, error::Error> {
        self.inner
            .call(
                route::PATH_AUTH_REFRESH_TOKEN,
                Method::POST,
                request,
                rpc::CallOptions { auth_disable: true },
            )
            .await
    }
}
